home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / xinetd / sio.1.5.6 / sio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-09  |  14.9 KB  |  687 lines

  1. /*
  2.  * (c) Copyright 1992 by Panagiotis Tsirigotis
  3.  * All rights reserved.  The file named COPYRIGHT specifies the terms 
  4.  * and conditions for redistribution.
  5.  */
  6.  
  7. static char RCSid[] = "$Id: sio.c,v 7.4 1992/11/05 00:45:26 panos Exp $" ;
  8. static char sio_version[] = VERSION ;
  9.  
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12.  
  13. #include "sio.h"
  14. #include "impl.h"
  15.  
  16. #ifdef EVENTS
  17. #include "events.h"
  18. #endif
  19.  
  20. /*
  21.  * SIO WRITE FUNCTIONS: Swrite, Sputc
  22.  */
  23.  
  24. /*
  25.  * Stream write call: arguments same as those of write(2)
  26.  */
  27. int Swrite( fd, addr, nbytes )
  28.     int fd ;
  29.     register char *addr ;
  30.     register int nbytes ;
  31. {
  32.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  33.     register __sio_od_t *odp = ODP( dp ) ;
  34.     register int b_transferred ;
  35.     register int b_avail ;
  36.     int total_b_transferred ;
  37.     int b_written ;
  38.     int b_in_buffer ;
  39.  
  40. #ifdef EVENTS
  41.     EVENT( fd, EV_SWRITE ) ;
  42. #endif
  43.  
  44.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  45.     ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
  46.  
  47.     b_avail = odp->buf_end - odp->nextb ;
  48.     b_transferred = MIN( nbytes, b_avail ) ;
  49.     sio_memcopy( addr, odp->nextb, b_transferred ) ;
  50.     odp->nextb += b_transferred ;
  51.  
  52.     /*
  53.      * check if we are done
  54.      */
  55.     if ( b_transferred == nbytes )
  56.         return( b_transferred ) ;
  57.  
  58.     /*
  59.      * at this point we know that the buffer is full
  60.      */
  61.     b_in_buffer = odp->buf_end - odp->start ;
  62.     b_written = __sio_writef( odp, fd ) ;
  63.     if ( b_written != b_in_buffer )
  64.         return( (b_written >= nbytes) ? nbytes : b_written ) ;
  65.     
  66.     total_b_transferred = b_transferred ;
  67.     addr += b_transferred ;
  68.     nbytes -= b_transferred ;
  69.  
  70.     for ( ;; )
  71.     {
  72.         b_transferred = MIN( nbytes, odp->buffer_size ) ;
  73.         sio_memcopy( addr, odp->nextb, b_transferred ) ;
  74.         odp->nextb += b_transferred ;
  75.         nbytes -= b_transferred ;
  76.         if ( nbytes == 0 )
  77.         {
  78.             total_b_transferred += b_transferred ;
  79.             break ;
  80.         }
  81.         /*
  82.          * the buffer is full
  83.          */
  84.         b_written = __sio_writef( odp, fd ) ;
  85.         if ( b_written != odp->buffer_size )
  86.         {
  87.             if ( b_written != SIO_ERR )
  88.             {
  89.                 total_b_transferred += b_written ;
  90.                 odp->nextb += b_written ;
  91.             }
  92.             break ;
  93.         }
  94.         /*
  95.          * everything is ok
  96.          */
  97.         total_b_transferred += b_transferred ;
  98.         addr += b_transferred ;
  99.     }
  100.     return( total_b_transferred ) ;
  101. }
  102.  
  103.  
  104. /*
  105.  * Add a character to a file
  106.  */
  107. int Sputc( fd, c )
  108.     int fd ;
  109.     char c ;
  110. {
  111.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  112.     register __sio_od_t *odp = ODP( dp ) ;
  113.  
  114. #ifdef EVENTS
  115.     EVENT( fd, EV_SPUTC ) ;
  116. #endif
  117.     
  118.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  119.     ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
  120.  
  121.     /*
  122.      * The following is a weak check since we should really be
  123.      * checking that nextb == buf_end (it would be an error for
  124.      * nextb to exceed buf_end; btw, the assertion above, when
  125.      * enabled makes sure this does not occur).
  126.      *
  127.      * NOTE: __sio_writef NEVER uses data beyond the end of buffer.
  128.      */
  129.     if ( odp->nextb >= odp->buf_end )
  130.     {
  131.         int b_in_buffer = odp->buf_end - odp->start ;
  132.  
  133.         /*
  134.          * There is nothing we can do if __sio_writef does not manage
  135.          * to write the whole buffer
  136.          */
  137.         if ( __sio_writef( odp, fd ) != b_in_buffer )
  138.             return( SIO_ERR ) ;
  139.     }
  140.     *odp->nextb++ = c ;
  141.     if ( __SIO_MUST_FLUSH( *odp, c ) && __sio_writef( odp, fd ) == SIO_ERR )
  142.         return( SIO_ERR ) ;
  143.     return ( c ) ;
  144. }
  145.  
  146.  
  147.  
  148. /*
  149.  * SIO READ FUNCTIONS
  150.  */
  151.  
  152. /*
  153.  * Stream read call: arguments same as those of read(2)
  154.  */
  155. int Sread( fd, addr, nbytes )
  156.     int fd ;
  157.     char *addr ;
  158.     int nbytes ;
  159. {
  160.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  161.     register __sio_id_t *idp = IDP( dp ) ;
  162.     register int b_transferred ;
  163.     int b_read ;
  164.     int total_b_transferred ;
  165.     int b_left ;
  166.  
  167. #ifdef EVENTS
  168.     EVENT( fd, EV_SREAD ) ;
  169. #endif
  170.  
  171.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  172.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  173.  
  174.     b_left = idp->end - idp->nextb ;
  175.     b_transferred = MIN( b_left, nbytes ) ;
  176.     sio_memcopy( idp->nextb, addr, b_transferred ) ;
  177.     idp->nextb += b_transferred ;
  178.     if ( b_transferred == nbytes )
  179.         return( b_transferred ) ;
  180.     
  181.     nbytes -= b_transferred ;
  182.     total_b_transferred = b_transferred ;
  183.     addr += b_transferred ;
  184.  
  185.     do
  186.     {
  187.         b_read = __sio_readf( idp, fd ) ;
  188.         switch ( b_read )
  189.         {
  190.             case SIO_ERR:
  191.                 if ( total_b_transferred == 0 )
  192.                     return( SIO_ERR ) ;
  193.                 /* FALL THROUGH */
  194.             
  195.             case 0:
  196.                 return( total_b_transferred ) ;
  197.         }
  198.             
  199.         b_transferred = MIN( b_read, nbytes ) ;
  200.         sio_memcopy( idp->nextb, addr, b_transferred ) ;
  201.         addr += b_transferred ;
  202.         idp->nextb += b_transferred ;
  203.         total_b_transferred += b_transferred ;
  204.         nbytes -= b_transferred ;
  205.     }
  206.     while ( nbytes && b_read == idp->buffer_size ) ;
  207.     return( total_b_transferred ) ;
  208. }
  209.  
  210.  
  211.  
  212. /*
  213.  * Read a line from a file
  214.  * Returns a pointer to the beginning of the line or NULL
  215.  */
  216. char *Srdline( fd )
  217.     int fd ;
  218. {
  219.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  220.     register __sio_id_t *idp = IDP( dp ) ;
  221.     register char *cp ;
  222.     register char *line_start ;
  223.     register int b_left ;
  224.     register int extension ;
  225.  
  226. #ifdef EVENTS
  227.     EVENT( fd, EV_SRDLINE ) ;
  228. #endif
  229.  
  230.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ;
  231.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  232.  
  233. #ifdef MEMORY_MAP
  234.     if ( idp->memory_mapped && __sio_switch( idp, fd ) == FAILURE )
  235.         return( NULL ) ;
  236. #endif
  237.  
  238.     b_left = idp->end - idp->nextb ;
  239.     /*
  240.      * Look for a '\n'. If the search fails, extend the buffer
  241.      * and search again (the extension is performed by copying the
  242.      * bytes that were searched to the auxiliary buffer and reading 
  243.      * new input in the main buffer).
  244.      * If the new input still does not contain a '\n' and there is
  245.      * more space in the main buffer (this can happen with network
  246.      * connections), read more input until either the buffer is full
  247.      * or a '\n' is found.
  248.      * Finally, set cp to point to the '\n', and line_start to
  249.      * the beginning of the line
  250.      */
  251.     if ( b_left && ( cp = sio_memscan( idp->nextb, b_left, '\n' ) ) != NULL )
  252.     {
  253.         line_start = idp->nextb ;
  254.         idp->nextb = cp + 1 ;
  255.     }
  256.     else
  257.     {
  258.         extension = __sio_extend_buffer( idp, fd, b_left ) ;
  259.         if ( extension > 0 )
  260.         {
  261.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  262.  
  263.             line_start = idp->start ;
  264.             cp = sio_memscan( idp->nextb, extension, '\n' ) ;
  265.             if ( cp != NULL )
  266.                 idp->nextb = cp + 1 ;
  267.             else
  268.                 for ( ;; )
  269.                 {
  270.                     idp->nextb = idp->end ;
  271.                     extension = __sio_more( idp, fd ) ;
  272.                     if ( extension > 0 )
  273.                     {
  274.                         cp = sio_memscan( idp->nextb, extension, '\n' ) ;
  275.                         if ( cp == NULL )
  276.                             continue ;
  277.                         idp->nextb = cp + 1 ;
  278.                         break ;
  279.                     }
  280.                     else
  281.                     {
  282.                         /*
  283.                          * If there is spare room in the buffer avoid trashing
  284.                          * the last character
  285.                          */
  286.                         if ( idp->end < &idp->buf[ idp->buffer_size ] )
  287.                             cp = idp->end ;
  288.                         else
  289.                             cp = &idp->buf[ idp->buffer_size - 1 ] ;
  290.                         break ;
  291.                     }
  292.                 }
  293.         }
  294.         else                    /* buffer could not be extended */
  295.             if ( b_left == 0 )
  296.             {
  297.                 /*
  298.                  * Set errno to 0 if EOF has been reached
  299.                  */
  300.                 if ( extension == 0 )
  301.                     errno = 0 ;
  302.                 return( NULL ) ;
  303.             }
  304.             else
  305.             {
  306.                 line_start = idp->start ;
  307.                 cp = idp->end ;
  308.                 /*
  309.                  * By setting idp->nextb to be equal to idp->end,
  310.                  * subsequent calls to Srdline will return NULL because
  311.                  * __sio_extend_buffer will be invoked and it will return 0.
  312.                  */
  313.                 idp->nextb = idp->end ;
  314.             }
  315.     }
  316.     *cp = NUL ;
  317.     idp->line_length = cp - line_start ;
  318.     return( line_start ) ;
  319. }
  320.  
  321.  
  322. /*
  323.  * Get a character from a file
  324.  */
  325. int Sgetc( fd )
  326.     int fd ;
  327. {
  328.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  329.     register __sio_id_t *idp = IDP( dp ) ;
  330.  
  331. #ifdef EVENTS
  332.     EVENT( fd, EV_SGETC ) ;
  333. #endif
  334.  
  335.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  336.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  337.     if ( idp->nextb >= idp->end )
  338.     {
  339.         register int b_read = __sio_readf( idp, fd ) ;
  340.  
  341.         if ( b_read == 0 )
  342.             return( SIO_EOF ) ;
  343.         else if ( b_read == SIO_ERR )
  344.             return( SIO_ERR ) ;
  345.     }
  346.     return( (int) *idp->nextb++ ) ;
  347. }
  348.  
  349.  
  350. char *Sfetch( fd, lenp )
  351.     int fd ;
  352.     long *lenp ;
  353. {
  354.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  355.     register __sio_id_t *idp = IDP( dp ) ;
  356.     register int b_read ;
  357.     register char *p ;
  358.  
  359. #ifdef EVENTS
  360.     EVENT( fd, EV_SFETCH ) ;
  361. #endif
  362.  
  363.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ;
  364.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  365.     if ( idp->nextb >= idp->end )
  366.     {
  367.         b_read = __sio_readf( idp, fd ) ;
  368.         if ( b_read == SIO_ERR )
  369.             return( NULL ) ;
  370.         if ( b_read == 0 )
  371.         {
  372.             errno = 0 ;
  373.             return( NULL ) ;
  374.         }
  375.     }
  376.     *lenp = idp->end - idp->nextb ;
  377.     p = idp->nextb ;
  378.     idp->nextb = idp->end ;
  379.     return( p ) ;
  380. }
  381.  
  382.  
  383.  
  384. /*
  385.  * SIO CONTROL FUNCTIONS
  386.  */
  387.  
  388. /*
  389.  * Undo the last Srdline or Sgetc
  390.  */
  391. int Sundo( fd, type )
  392.     int fd ;
  393.     int type ;
  394. {
  395.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; 
  396.     register __sio_id_t *idp = IDP( dp ) ;
  397.     int retval = 0 ;
  398.  
  399. #ifdef EVENTS
  400.     EVENT( fd, EV_SUNDO ) ;
  401. #endif
  402.  
  403.     CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  404.  
  405.     /*
  406.      * Undo works only for fd's used for input
  407.      */
  408.     if ( dp->stream_type != __SIO_INPUT_STREAM )
  409.         return( SIO_ERR ) ;
  410.  
  411.     /*
  412.      * Check if the operation makes sense; if so, do it, otherwise ignore it
  413.      */
  414.     switch ( type )
  415.     {
  416.         case SIO_UNDO_LINE:
  417.             if ( idp->nextb - idp->line_length > idp->start )
  418.             {
  419.                 *--idp->nextb = '\n' ;
  420.                 idp->nextb -= idp->line_length ;
  421.             }
  422.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  423.             break ;
  424.         
  425.         case SIO_UNDO_CHAR:
  426.             if ( idp->nextb > idp->start )
  427.                 idp->nextb-- ;
  428.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  429.             break ;
  430.         
  431.         default:
  432.             retval = SIO_ERR ;
  433.             break ;
  434.     }
  435.     return( retval ) ;
  436. }
  437.  
  438.  
  439. /*
  440.  * Flush the buffer associated with the given file descriptor
  441.  * The special value, SIO_FLUSH_ALL flushes all buffers
  442.  *
  443.  * Return value:
  444.  *            0 :  if fd is SIO_FLUSH_ALL or if the flush is successful
  445.  *        SIO_ERR: if fd is not SIO_FLUSH_ALL and
  446.  *                                the flush is unsuccessful
  447.  *                            or the descriptor is not initialized or it is not 
  448.  *                                an output descriptor
  449.  */
  450. int Sflush( fd )
  451.     int fd ;
  452. {
  453.    register __sio_descriptor_t *dp ;
  454.    int b_in_buffer ;
  455.  
  456. #ifdef EVENTS
  457.     EVENT( fd, EV_SFLUSH ) ;
  458. #endif
  459.  
  460.    if ( fd == SIO_FLUSH_ALL )
  461.    {
  462.       for ( fd = 0, dp = __sio_descriptors ;
  463.                 fd < N_SIO_DESCRIPTORS ;
  464.                 dp++, fd++ )
  465.          if ( DESCRIPTOR_INITIALIZED( dp ) &&
  466.                             dp->stream_type == __SIO_OUTPUT_STREAM )
  467.             (void) __sio_writef( ODP( dp ), fd ) ;
  468.       return( 0 ) ;
  469.    }
  470.    else
  471.    {
  472.       dp = &__sio_descriptors[ fd ] ;
  473.  
  474.         CONTROL_SETUP( dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  475.       b_in_buffer = ODP( dp )->nextb - ODP( dp )->start ;
  476.       if ( __sio_writef( ODP( dp ), fd ) != b_in_buffer )
  477.          return( SIO_ERR ) ;
  478.       else
  479.          return( 0 ) ;
  480.    }
  481. }
  482.  
  483.  
  484. /*
  485.  * Close the file descriptor. This call is provided because
  486.  * a file descriptor may be closed and then reopened. There is
  487.  * no easy way for SIO to identify such a situation, so Sclose
  488.  * must be used.
  489.  *
  490.  * Sclose invokes Sdone which finalizes the buffer.
  491.  * There is no SIO_CLOSE_ALL value for fd because such a thing
  492.  * would imply that the program will exit very soon, therefore
  493.  * the closing of all file descriptors will be done in the kernel
  494.  * (and the finalization will be done by the finalization function
  495.  * NOTE: not true if the OS does not support a finalization function)
  496.  *
  497.  * There is no need to invoke SETUP; Sdone will do it.
  498.  */
  499. int Sclose( fd )
  500.     int fd ;
  501. {
  502. #ifdef EVENTS
  503.     EVENT( fd, EV_SCLOSE ) ;
  504. #endif
  505.  
  506.     if ( __SIO_FD_INITIALIZED( fd ) )
  507.         if ( Sdone( fd ) == SIO_ERR )
  508.             return( SIO_ERR ) ;
  509.     return( close( fd ) ) ;
  510. }
  511.  
  512.  
  513.  
  514. /*
  515.  * Tie the file descriptor in_fd to the file descriptor out_fd
  516.  * This means that whenever a read(2) is done on in_fd, it is
  517.  * preceded by a write(2) on out_fd.
  518.  * Why this is nice to have:
  519.  *     1) When used in filters it maximizes concurrency
  520.  *        2) When the program prompts the user for something it forces
  521.  *            the prompt string to be displayed even if it does not
  522.  *            end with a '\n' (which would cause a flush).
  523.  * In this implementation, out_fd cannot be a regular file.
  524.  * This is done to avoid non-block-size write's.
  525.  * The file descriptors are initialized if that has not been done
  526.  * already. If any of them is initialized, it must be for the appropriate
  527.  * stream type (input or output).
  528.  *
  529.  * NOTE: the code handles correctly the case when in_fd == out_fd
  530.  */
  531. int Stie( in_fd, out_fd )
  532.     int in_fd, out_fd ;
  533. {
  534.     struct stat st ;
  535.     register __sio_descriptor_t *dp ;
  536.     int was_initialized ;
  537.     boolean_e failed = NO ;
  538.  
  539. #ifdef EVENTS
  540.     EVENT( in_fd, EV_STIE ) ;
  541. #endif
  542.  
  543.     /*
  544.      * Check if the out_fd is open
  545.      */
  546.     if ( fstat( out_fd, &st ) == -1 )
  547.         return( SIO_ERR ) ;
  548.  
  549.     /*
  550.      * If the out_fd refers to a regular file, the request is silently ignored
  551.      */
  552.     if ( ( st.st_mode & S_IFMT ) == S_IFREG )
  553.         return( 0 ) ;
  554.     
  555.     dp = &__sio_descriptors[ in_fd ] ;
  556.     was_initialized = dp->initialized ;        /* remember if it was initialized */
  557.     IO_SETUP( in_fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  558.  
  559.     /*
  560.      * Perform manual initialization of out_fd to avoid leaving in_fd
  561.      * initialized if the initialization of out_fd fails.
  562.      * If out_fd is initialized, check if it is used for output.
  563.      * If it is not initialized, initialize it for output.
  564.      */
  565.     dp = &__sio_descriptors[ out_fd ] ;
  566.     if ( DESCRIPTOR_INITIALIZED( dp ) )
  567.     {
  568.         if ( dp->stream_type != __SIO_OUTPUT_STREAM )
  569.         {
  570.             failed = YES ;
  571.             errno = EBADF ;
  572.         }
  573.     }
  574.     else
  575.         if ( __sio_init( dp, out_fd, __SIO_OUTPUT_STREAM ) == SIO_ERR )
  576.             failed = YES ;
  577.  
  578.     if ( failed == NO )
  579.     {
  580.         __SIO_ID( in_fd ).tied_fd = out_fd ;
  581.         return( 0 ) ;
  582.     }
  583.     else
  584.     {
  585.         /*
  586.          * We failed. If we did any initialization, undo it
  587.          */
  588.         if ( ! was_initialized )
  589.         {
  590.             int save_errno = errno ;
  591.  
  592.             (void) Sdone( in_fd ) ;
  593.             errno = save_errno ;
  594.         }
  595.         return( SIO_ERR ) ;
  596.     }
  597. }
  598.  
  599.  
  600. /*
  601.  * Untie a file descriptor
  602.  */
  603. int Suntie( fd )
  604.     int fd ;
  605. {
  606.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  607.  
  608. #ifdef EVENTS
  609.     EVENT( fd, EV_SUNTIE ) ;
  610. #endif
  611.  
  612.     CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  613.     
  614.     if ( IDP( dp )->tied_fd != SIO_NO_TIED_FD )
  615.     {
  616.         IDP( dp )->tied_fd = SIO_NO_TIED_FD ;
  617.         return( 0 ) ;
  618.     }
  619.     else
  620.     {
  621.         errno = EBADF ;
  622.         return( SIO_ERR ) ;
  623.     }
  624. }
  625.  
  626.  
  627. /*
  628.  * Changes the type of buffering on the specified descriptor.
  629.  * As a side-effect, it initializes the descriptor as an output stream.
  630.  */
  631. int Sbuftype( fd, type )
  632.     int fd, type ;
  633. {
  634.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  635.  
  636. #ifdef EVENTS
  637.     EVENT( fd, EV_SBUFTYPE ) ;
  638. #endif
  639.  
  640.     /*
  641.      * Check for a valid type
  642.      */
  643.     if ( type != SIO_LINEBUF && type != SIO_FULLBUF && type != SIO_NOBUF )
  644.     {
  645.         errno = EINVAL ;
  646.         return( SIO_ERR ) ;
  647.     }
  648.  
  649.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  650.     ODP( dp )->buftype = type ;
  651.     return( 0 ) ;
  652. }
  653.  
  654.  
  655. #ifndef sio_memscan
  656.  
  657. PRIVATE char *sio_memscan( from, how_many, ch )
  658.    char *from ;
  659.    int how_many ;
  660.    register char ch ;
  661. {
  662.    register char *p ;
  663.    register char *last = from + how_many ;
  664.  
  665.    for ( p = from ; p < last ; p++ )
  666.       if ( *p == ch )
  667.          return( p ) ;
  668.       return( 0 ) ;
  669. }
  670.  
  671. #endif    /* sio_memscan */
  672.  
  673.  
  674. #ifdef NEED_MEMCOPY
  675.  
  676. void __sio_memcopy( from, to, nbytes )
  677.    register char *from, *to ;
  678.    register int nbytes ;
  679. {
  680.    while ( nbytes-- )
  681.       *to++ = *from++ ;
  682. }
  683.  
  684. #endif /* NEED_MEMCOPY */
  685.  
  686.  
  687.